
package trim.testalgorithms.optresttrim;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import trim.common.Format;
import trim.common.Material;
import trim.common.inputdata.FormatOrder;
import trim.common.inputdata.MaterialStock;
import trim.differentmaterials.core.NoSolutionsException;

/**
 *
 * @author Podorov Alexander
 */

public class FirstTrimmer
{
    private MaterialStock[] materials;
    private FormatOrder[] formats;
    private List<MaterialStock[]> materialsCombination;
    private int upperBound;
    private int lowerBound;
    private boolean isFiniteMaterials;

    public FirstTrimmer(MaterialStock[] materials, FormatOrder[] formats, boolean isFiniteMaterials)
    {
        this.materials = materials;
        this.formats = formats;
        materialsCombination = new ArrayList<MaterialStock[]>();
        this.isFiniteMaterials = isFiniteMaterials;
    }

    private boolean isNotClearFormats()
    {
        boolean result = false;
        for (int i = 0; i < formats.length; i++)
        {
            if (formats[i].getCount() > 0)
            {
                result = true;
                break;
            }
        }
        return result;
    }

    private boolean isNotClearMaterials()
    {
        boolean result = false;
        for (int i = 0; i < materials.length; i++)
        {
            if (materials[i].getCount() > 0)
            {
                result = true;
                break;
            }
        }
        return result;
    }

    /*private boolean isNotSmallMaterial(Material material)
    {
        boolean result = false;
        for (int i = 0; i < formats.length; i++)
        {
            if (material.getLength() > formats[i].getFormat().getLength())
            {
                result = true;
                break;
            }
        }
        return result;
    }*/

    private void computeLowerBound()
    {
        for (int i = 0; i < formats.length; i++)
        {
            lowerBound += formats[i].getFormat().getLength() * formats[i].getCount();

           // int f=0;
           // System.out.println(formats[i].getFormat().getLength() + " - " + formats[i].getCount());
        }
    }

    private void computeUpperBound() throws NoSolutionsException
    {
        if (isFiniteMaterials)
        {
            MaterialStock[] restore = new MaterialStock[materials.length];
            for (int i = 0; i < materials.length; i++)
            {
                restore[i] = new MaterialStock(new Material(materials[i].getMaterial()), materials[i].getCount());
            }

            while (isNotClearFormats() && isNotClearMaterials())
            {
                int record = Integer.MAX_VALUE;
                int index = - 1;
                FormatOrder[] currentSolution = null;
                FormatOrder[] solution = null;
                Material material = null;

                for (int i = 0; i < materials.length; i++)
                {
                    if (materials[i].getCount() > 0) //&& (isNotSmallMaterial(materials[i].getMaterial())))
                    {
                        currentSolution = bestSubsetSum(materials[i].getMaterial());

                        int value = 0;
                        for (int j = 0; j < currentSolution.length; j++)
                        {
                            value += currentSolution[j].getFormat().getLength() * currentSolution[j].getCount();

                            //System.out.println(value);
                        }

                        if (materials[i].getMaterial().getLength() - value < record)
                        {
                            //record = value;


                            record = materials[i].getMaterial().getLength() - value;
                            solution = currentSolution;
                            //System.out.println(materials[i].getMaterial().getLength());

                            material = materials[i].getMaterial();
                            index = i;
                        }
                    }
                }


                //System.out.println(materials[index].getMaterial().getLength());
                //int ffff=0;
                //for (int f=0; f < solution.length; f++)
                //{
                //    System.out.print(solution[f].getCount() + " ");
                //    ffff += solution[f].getCount() * solution[f].getFormat().getLength();
               // }
               // System.out.println("   " + materials[index].getMaterial().getLength() + "ac = " + ffff);


                materials[index].setCount(materials[index].getCount() - 1);

                for (int i = 0; i < formats.length; i++)
                {
                    formats[i].setCount(formats[i].getCount() - solution[i].getCount());
                }

                upperBound += material.getLength();
            }

            materials = restore;

            if (isNotClearFormats())
            {
                throw new NoSolutionsException();
            }
        }
        else
        {
            while (isNotClearFormats())
            {
                int record = Integer.MAX_VALUE;
                FormatOrder[] currentSolution = null;
                FormatOrder[] solution = null;
                Material material = null;

                for (int i = 0; i < materials.length; i++)
                {
                    currentSolution = bestSubsetSum(materials[i].getMaterial());

                    int value = 0;
                    for (int j = 0; j < solution.length; j++)
                    {
                        value += currentSolution[j].getFormat().getLength() * currentSolution[j].getCount();
                    }

                    if (materials[i].getMaterial().getLength() - value < record)
                    {
                        //record = value;
                        record = materials[i].getMaterial().getLength() - value;

                        solution = currentSolution;

                        material = materials[i].getMaterial();
                    }
                }

                for (int i = 0; i < formats.length; i++)
                {
                    formats[i].setCount(formats[i].getCount() - solution[i].getCount());
                }

                upperBound += material.getLength();
            }
        }
    }

    public void fillMaterialsList() throws NoSolutionsException
    {
        computeLowerBound();
        computeUpperBound();

        int tail = upperBound;
        int length = tail;        
        int value = 0;        
        int dimension = materials.length;
        int[] items = new int[dimension];        
        int current = 0;
        int lastMaterial = 0;
        int[] list = new int[dimension];
        int last = - 1;
        boolean exit = false;


        int hhhhh=0;


        do
        {
            for (int i = current; i < dimension; i++)
            {
                if (isFiniteMaterials)
                {
                    items[i] = Math.min(tail / materials[i].getMaterial().getLength(), materials[i].getCount());
                }
                else
                {
                    items[i] = tail / materials[i].getMaterial().getLength();
                }

                if (items[i] > 0)
                {
                    tail -= items[i] * materials[i].getMaterial().getLength();
                    value = length - tail;                    
                    list[++last] = i;
                }
            }

            if (value >= lowerBound)
            {
                MaterialStock[] materialStocks = new MaterialStock[materials.length];
                for (int i = 0; i < materials.length; i++)
                {
                    materialStocks[i] = new MaterialStock(materials[i].getMaterial(), items[i]);
                }
                materialsCombination.add(materialStocks);


                System.out.println(hhhhh++ + "  -->" + value);
            }
            
            if (last >= 0)
            {
                lastMaterial = list[last];
                if (items[lastMaterial] == 1)
                {
                    last--;
                }
                items[lastMaterial]--;
                tail += materials[lastMaterial].getMaterial().getLength();
                value = length - tail;                    
                current = lastMaterial + 1;
            }
            else
            {
                exit = true;
            }
            
        }
        while (!exit);        

        Collections.sort(materialsCombination, new Comparator<MaterialStock[]>()
        {
            public int compare(MaterialStock[] materialStock1, MaterialStock[] materialStock2) {

                int sum1 = 0;
                for (int i = 0; i < materialStock1.length; i++)
                {
                    sum1 += materialStock1[i].getMaterial().getLength() * materialStock1[i].getCount();
                }

                int sum2 = 0;
                for (int i = 0; i < materialStock2.length; i++)
                {
                    sum2 += materialStock2[i].getMaterial().getLength() * materialStock2[i].getCount();
                }

                return sum1 - sum2;
            }
        });
    }

    public List<MaterialStock[]> getCombination()
    {
        return materialsCombination;
    }

    private FormatOrder[] bestSubsetSum(Material material)
    {
        int tail = material.getLength();
        int length = tail;
        int knifes = material.getKnifeCount();
        int count = 0;
        int value = 0;
        int record = 0;
        int dimension = formats.length;
        int[] items = new int[dimension];
        int[] bestItems = new int[dimension];
        int current = 0;
        int lastFormat = 0;
        int[] list = new int[dimension];
        int last = - 1;
        boolean exit = false;

        do
        {
            for (int i = current; i < dimension; i++)
            {
                items[i] = Math.min(tail / formats[i].getFormat().getLength(), Math.abs(formats[i].getCount()));

                if (items[i] > 0)
                {
                    tail -= items[i] * formats[i].getFormat().getLength();
                    value = length - tail;
                    count += items[i];
                    list[++last] = i;
                }
            }
            if ((record < value) && (count <= knifes))
            {
                record = value;
                for (int i = 0; i < dimension; i++)
                {
                    bestItems[i] = items[i];
                }
            }
            if (record == length)
            {
                exit = true;
            }
            else
            {
                if (last >= 0)
                {
                    lastFormat = list[last];
                    if (items[lastFormat] == 1)
                    {
                        last--;
                    }
                    items[lastFormat]--;
                    tail += formats[lastFormat].getFormat().getLength();
                    value = length - tail;
                    count--;
                    current = lastFormat + 1;
                }
                else
                {
                    exit = true;
                }
            }
        }
        while (!exit);        

        FormatOrder[] result = new FormatOrder[dimension];
        for (int i = 0; i < dimension; i++)
        {
            result[i] = new FormatOrder(new Format(formats[i].getFormat()), bestItems[i]);
        }

        return result;
    }
}
